/**
 * @author ming.yan-1
 * @version V1.0
 * @Title PACKAGE_NAME
 * @Description
 * @date 2018/10/7  下午7:08
 */
public class SnowFlake {

    private final static long START_STAMP = 1538911122809L;

    //机器编号的占位，小公司的集群少，用不到5位那么多，2的5次方就32了
    private final static long MACHINE_ID_BIT = 5L;

    //所谓的数据位暂时不懂什么意思，可能跟集群部署有关系吧
    private final static long Data_ID_BIT = 5L;

    //毫秒内，自增的序列，2的12次方是4096，相当于单台机器每毫秒可以产生4096个id不重复
    private final static long SEQUEUE_BIT = 12L;

    //时间戳占的位数
    private final static long STAMP_BIT = 41L;

    //机器的编号每次移动的位数
    private final static long MACHINE_MOVE = SEQUEUE_BIT;

    private final static long DATA_MOVE = SEQUEUE_BIT + MACHINE_ID_BIT;

    //时间戳每次移动的位数
    private final static long STAMP_MOVE = SEQUEUE_BIT + MACHINE_ID_BIT + Data_ID_BIT;

    //sequene的最大值,SEQUEUE_BIT为5时，最大值是11111，
    private final static long MAX_SEQUENE = ~(-1L << SEQUEUE_BIT);

    //机器编号的最大值
    private final static long max_machine = ~(-1L << MACHINE_ID_BIT);

    private long machineId;

    private long dataId;

    //sequene的初始值
    private long sequeneId = 0L;

    private long lastStamp = -1L;

    public SnowFlake(long machineId, long dataId) {
        if(machineId>max_machine||machineId<0){
            throw new IllegalArgumentException("machind id must between 0 and max machine");
        }
        if(dataId>max_machine||dataId<0){
            throw new IllegalArgumentException("data id must between 0 and max machine");
        }
        this.machineId = machineId;
        this.dataId = dataId;
    }

    /**
     * SnowFlake的结构如下(每部分用-分开):<br>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * 1位标识，由于long基本类型在Java中是带符号的，最高位是符号位，正数是0，负数是1，所以id一般是正数，最高位是0
     * 41位时间截(毫秒级)，注意，41位时间截不是存储当前时间的时间截，而是存储时间截的差值（当前时间截 - 开始时间截)
     * 得到的值），这里的的开始时间截，一般是我们的id生成器开始使用的时间，由我们程序来指定的（如下下面程序IdWorker类的startTime属性）。41位的时间截，可以使用69年，年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
     * 10位的数据机器位，可以部署在1024个节点，包括5位dataid和5位machineid
     * 12位序列，毫秒内的计数，12位的计数顺序号支持每个节点每毫秒(同一机器，同一时间截)产生4096个ID序号
     * 加起来刚好64位，为一个Long型。
     * SnowFlake的优点是，整体上按照时间自增排序，并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)，并且效率较高，经测试，SnowFlake每秒能够产生26万ID左右。
     */
    public synchronized long getNext(){
        long currentStamp = System.currentTimeMillis();
        if(currentStamp == lastStamp){
                sequeneId = sequeneId+1;
                if(sequeneId == MAX_SEQUENE){
                    //已经达到最大值
                }
        }else {
            sequeneId = 0L;
        }

        lastStamp = currentStamp;

        return (currentStamp - START_STAMP)<< STAMP_MOVE
                | machineId << MACHINE_MOVE
                | dataId << DATA_MOVE
                |sequeneId;
    }

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(10,23);
        int i = 0;
        for(;i<10000;i++){
            System.out.println(snowFlake.getNext());
        }
    }
}
